﻿using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.WindowsAzure.StorageClient;
using AntsCode.StorageClientExtensions.Helpers;
using System.Data.Services.Client;

namespace AntsCode.StorageClientExtensions.QueryHandlers
{
    /// <summary>
    /// Handles a Where query
    /// </summary>
    /// <typeparam name="T">The source type.</typeparam>
    internal class WhereQueryHandler<T> : QueryHandler
    {
        public WhereQueryHandler(TableServiceContext context, QueryExecutor queryExecutor, Expression e, MethodCallExpression m, IQueryable<T> data, ContinuationToken continuationToken)
            : base(data, continuationToken)
        {
            IQueryable<T> rawData;

            if (data == null)
            {
                string entitySetName = GetValueFromConstant<IXTableServiceQuery>(m.Arguments[0]).EntitySetName;
                rawData = context.CreateQuery<T>(entitySetName);
            }
            else
            {
                rawData = data;
            }

            // Get data
            var expr = GetExprFromUnary<Expression<Func<T, bool>>>(m.Arguments[1]);

            if (rawData.GetType().IsSubclassOf(typeof(DataServiceQuery<T>)))
            {
                // To address an issue with the standard TableServiceContext where values are not escaped before sending
                // to table storage, we use the ExpressionConverter which escapes all constants
                // (known issue: method calls that return strings will not be escaped)
                ExpressionConverter<T, T> binaryExpressionConverter = new ExpressionConverter<T, T>() { DoTypeConversion = false };
                expr = (Expression<Func<T, bool>>)binaryExpressionConverter.ConvertExpression(expr);
            }

            // Apply the where method
            rawData = rawData.Where(expr);

            // Execute the query if not done already
            if (rawData.GetType().IsSubclassOf(typeof(DataServiceQuery<T>)))
            {
                // Suffix any additional methods from the parent expression (First(), FirstOrDefault() etc)
                // This must be done before we execute the query for the methods to be applied to the Table Service Query.
                var query = SuffixQuery.SuffixExtras(e, (DataServiceQuery<T>)rawData);

                // Execute the query
                queryExecutor.IncrementQueryBatchNo();
                var response = queryExecutor.ExecuteAll(query, ref continuationToken);

                // Update the continuation token
                this.ContinuationToken = continuationToken;

                rawData = response.ToList().AsQueryable();
            }

            this.Data = rawData;
        }
    }
}
